
__author__="unlotto ... at ... gmail ... com"

#VARHOLDER is just a  "char[] pointer to python string cython precaution"#
VARHOLDER = ""

MINDELAY=0.05 #minimum delay for while loops that try to give commands to the cam

import time

cdef extern from "linux/videodev2.h":
  #from videodev2 we need a few structures and a few definitions#

  cdef struct v4l2_ext_control:
    #an id/value structure that works with pan and tilt#
    unsigned int id
    unsigned int reserved2[2]

    #this is a union in the c code with 64bit and 32bit signed int aswell as a reserved soemthing something#
    signed int value


  cdef struct v4l2_ext_controls:
    #the final struct for use with ioctl.
    v4l2_ext_control *controls
    unsigned int count
    unsigned int ctrl_class
    unsigned int error_idx
    unsigned int reserved[2]


  #registers as defined in videodev2.h for v4l2 external controls pan and tilt#
  unsigned int V4L2_CID_PAN_RELATIVE
  unsigned int V4L2_CID_TILT_RELATIVE
  unsigned int V4L2_CID_TILT_RESET
  unsigned int V4L2_CID_PAN_RESET

  #
  cdef int VIDIOC_S_EXT_CTRLS

cdef extern from "sys/ioctl.h":
  #ioctl has open and close functions aswell as ioctl itself for command parsing#
  int ioctl(int dev, int request, v4l2_ext_controls * argp)
  int open(char *pathname, int flags)
  int close(int fd)

cdef extern from "stdio.h":
  void perror(char *s)

cdef int _opendev(char * devpath):
  #open device with string devpath, it has to be a pointer#
  dev = open(devpath, 0)
  return dev

def opendev(devpath):
  #open device with devpath (python function)#
  VARHOLDER = devpath
  cdef char * devpoint = VARHOLDER
  return _opendev(devpoint)


cdef _closedev(int dev):
  close(dev)

def closedev(dev):
  _closedev(dev)


cdef int _pantilt(int dev, int pan,int tilt,int reset):
  cdef v4l2_ext_control xctrls[2]
  cdef v4l2_ext_controls ctrls

  if (reset > 0):
    if reset == 1:
      xctrls[0].id = V4L2_CID_PAN_RESET;
      xctrls[0].value = 1;
      xctrls[1].id = V4L2_CID_TILT_RESET;
      xctrls[1].value = 0;
    elif reset == 2:
      xctrls[0].id = V4L2_CID_TILT_RESET;
      xctrls[0].value = 1;
      xctrls[1].id = V4L2_CID_PAN_RESET;
      xctrls[1].value = 1;
      
  else:
    
    xctrls[0].id = V4L2_CID_PAN_RELATIVE;
    xctrls[0].value = pan;

    xctrls[1].id = V4L2_CID_TILT_RELATIVE;
    xctrls[1].value = tilt;


  ctrls.count = 2;
  ctrls.controls = xctrls;
   
  cdef int ioctlreturn = -1
  ioctlreturn =  ioctl(dev, VIDIOC_S_EXT_CTRLS, &ctrls)
  if (ioctlreturn < 0 ):
    perror("Something went wrong!\n Maybe Extension controls for the driver are not inserted. Use libwebcam or guvcview for that\n Or maybe wrong /dev/videoX descriptor!")

  return ioctlreturn

def pan(int dev, int steps):
  #Pans pan-amount of steps##
  steps = steps*64

  retval = 0
  while retval != -1:
    retval = _pantilt(dev,steps,0,0)
    time.sleep(MINDELAY)

  return retval

def tilt(int dev,int steps):
  #Tilts tilt-amount of steps##
  steps = steps*64

  retval = 0
  while retval != -1:
    retval = _pantilt(dev,0,steps,0)
    time.sleep(MINDELAY)


  return retval

def pantilt(int dev,int psteps, int tsteps):
  #Tilt AND pan x,y steps#
  tsteps = tsteps*64
  psteps = psteps*64

  retval = 0
  while retval != -1:
    retval = _pantilt(dev,psteps,tsteps,0)
    time.sleep(MINDELAY)


  return retval

def reset(int dev):
  retvalPan = _pantilt(dev,0,0,1)
  time.sleep(2)
  retvalTilt = _pantilt(dev,0,0,2)
  time.sleep(2)

  if retvalPan and retvalTilt :
    return 1
  else:
    return -1

class Lpantilt():
  """A more Pythonic class for doing the same pan,tilt instructions"""
  dev = -1 #device descriptor
  wait = True #Wait for device based on a calculated delay on number of steps taken

  def __init__(self,devpath):
    """Constructor sets self.dev descriptor"""

    self.opendev(devpath)

    if self.dev <= 0:
      print "FAILED TO OPEN DEVICE DESCRIPTOR"

  def delay(self,steps):
    #this needs some work. calc some kind of delay.#
    if steps < 0:
      steps = steps*-1
    if steps <= 4:
      d = steps * 0.25

    d = float(steps)/float(18)
    if d > 1.4:
      d = 1.4
    time.sleep(d)

  def pan(self,int steps):
    #returns list with command returnvalue and number of command injetion failures#
    retval = pan(self.dev,steps)

    if self.wait:
      self.delay(steps)

    return retval

  def tilt(self,int steps):
    #returns list with command returnvalue and number of command injetion failures#
    retval = tilt(self.dev,steps)

    if self.wait:
      self.delay(steps)

    return retval

  def reset(self):
    #returns 1 if success, otherwise -1
    return reset(self.dev)

  def pantilt(self,psteps,tsteps):
    ##
    retval = pantilt(self.dev,psteps,tsteps)

    if self.wait:
      if psteps > tsteps:
        self.delay(psteps)
      else:
        self.delay(tsteps)
    return retval

  def opendev(self,path):
    self.dev = opendev(path)
